package bleudp

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"iot-ble-server/global/globalconstants"
	"iot-ble-server/global/globallogger"
	"iot-ble-server/global/globalmemo"
	"iot-ble-server/global/globalredis"
	"iot-ble-server/global/globalstruct"
	"iot-ble-server/global/globalutils"
	"iot-ble-server/internal/config"
	"iot-ble-server/internal/packets"
	"iot-ble-server/internal/storage"
	"time"
)

//特征发现, 但是在有了指定UUID发现以后，这个函数的功能可以暂时搁置
// func ResumeCharacterFind(ctx context.Context, devMac, startHandle, endHandle string) error {
// 	var (
// 		err      error
// 		terminal globalstruct.TerminalInfo
// 		byteInfo []byte
// 		strInfo  string
// 	)
// 	if config.C.General.UseRedis {
// 		strInfo, err = globalredis.RedisCache.HGet(ctx, globalconstants.BleDevInfoCachePrefix, devMac).Result()
// 		if err != nil {
// 			if err != redis.Nil {
// 				globallogger.Log.Errorf("<ResumeCharacterFind> DevEui %s has redis error %v\n", devMac, err)
// 			} else {
// 				globallogger.Log.Errorf("<ResumeCharacterFind> DevEui %s absence terminal information %v\n", devMac, err)
// 			}
// 			return errors.New("cache has error")
// 		}
// 		byteInfo = []byte(strInfo)

// 	} else {
// 		byteInfo, err = globalmemo.BleFreeCacheDevInfo.Get([]byte(devMac))
// 		if err != nil {
// 			globallogger.Log.Errorf("<ResumeCharacterFind> DevEui %s has cache error %v\n", devMac, err)
// 			return errors.New("cache has error")
// 		}
// 	}
// 	err = json.Unmarshal(byteInfo, &terminal)
// 	if err != nil {
// 		return err
// 	}
// 	jsonInfo, err1 := GenerateCharacterFind(ctx, terminal, globalstruct.CharacterInfo{
// 		DevMac:      devMac,
// 		StartHandle: startHandle,
// 		EndHandle:   endHandle,
// 	})
// 	if err1 != nil {
// 		return err1
// 	}
// 	byteToSend := EnCodeForDownUdpMessage(jsonInfo)
// 	curSN, _ := strconv.ParseInt(jsonInfo.MessageAppHeader.SN, 16, 16)
// 	SendMsgBeforeDown(ctx, byteToSend, int(curSN), jsonInfo.MessageBody.GwMac+jsonInfo.MessageBody.ModuleID, terminal.GwMac, packets.TLVConnectMsg)
// 	return err
// }

//主服务发现,已经建立连接
func SearchMainService(ctx context.Context, terminal globalstruct.TerminalInfo) error {
	jsonInfo, err := GenerateDownJsonInfo(ctx, &globalstruct.DownJsonInfoUnify{
		GwMac:              terminal.GwMac,
		IotModuleId:        terminal.IotModuleId,
		TerminalMac:        terminal.TerminalMac,
		LinkedMsgType:      packets.IotAccessMsg,
		ApplicationMsgType: packets.BleRequest,
		TlvMsgType:         packets.TLVMainServiceReqMsg,
		Ctrl:               globalconstants.CtrlAllMsg,
	})
	if err != nil {
		return err
	}
	byteToSend, err := EnCodeForDownUdpMessage(jsonInfo)
	globalutils.BytesToHex(byteToSend)
	if err != nil {
		return errors.New("<SearchMainService> generate byte info failed" + err.Error())
	}
	err = SendMsgBeforeDown(ctx, byteToSend, jsonInfo.MessageHeader.LinkMsgFrameSN, jsonInfo.MessageAppHeader.SN, terminal.TerminalMac, terminal.GwMac, jsonInfo.MessageBody.ModuleID, packets.TLVMainServiceReqMsg)
	return err
}

//针对特定uuid的主服务发现
func SearchMainServiceUUID(ctx context.Context, terminal globalstruct.TerminalInfo, UUID string) error {
	jsonInfo, err := GenerateDownJsonInfo(ctx, &globalstruct.DownJsonInfoUnify{
		GwMac:              terminal.GwMac,
		IotModuleId:        terminal.IotModuleId,
		TerminalMac:        terminal.TerminalMac,
		UUID:               UUID,
		LinkedMsgType:      packets.IotAccessMsg,
		ApplicationMsgType: packets.BleRequest,
		TlvMsgType:         packets.TLVMainServiceByUUIDReqMsg,
		Ctrl:               globalconstants.CtrlAllMsg,
	})
	if err != nil {
		return err
	}
	byteToSend, err := EnCodeForDownUdpMessage(jsonInfo)
	globalutils.BytesToHex(byteToSend)
	if err != nil {
		return errors.New("<SearchMainServiceUUID> generate byte info failed" + err.Error())
	}
	err = SendMsgBeforeDown(ctx, byteToSend, jsonInfo.MessageHeader.LinkMsgFrameSN, jsonInfo.MessageAppHeader.SN, terminal.TerminalMac, terminal.GwMac, terminal.IotModuleId, packets.TLVMainServiceByUUIDReqMsg)
	return err
}

//针对特征配置下发
func CharacterConfig(ctx context.Context, terminal globalstruct.TerminalInfo, curHandleForChar map[string]globalstruct.ServiceCharacterNode) error {
	for _, node := range curHandleForChar {
		if node.CharUUID == globalconstants.NusCharacterUUIDSecond || (len(node.CharUUID) == 32 && node.CharUUID[4:8] == globalconstants.NusCharacterUUIDSecond) {
			globalmemo.NusCharacterDevForHandle.Set(terminal.TerminalMac, node.CharacterHandle) //特殊处理
			jsonInfo, err := GenerateDownJsonInfo(ctx, &globalstruct.DownJsonInfoUnify{
				TerminalMac:        terminal.TerminalMac,
				GwMac:              terminal.GwMac,
				IotModuleId:        terminal.IotModuleId,
				LinkedMsgType:      packets.IotAccessMsg,
				ApplicationMsgType: packets.BleRequest,
				TlvMsgType:         packets.TLVCharConfReqMsg,
				ServiceNode:        node,
				Ctrl:               globalconstants.CtrlAllMsg,
			})
			if err != nil {
				return err
			}
			byteToSend, err := EnCodeForDownUdpMessage(jsonInfo)
			globalutils.BytesToHex(byteToSend)
			if err != nil {
				return errors.New("<CharacterConfig> generate byte info failed" + err.Error())
			}
			SendMsgBeforeDown(ctx, byteToSend, jsonInfo.MessageHeader.LinkMsgFrameSN, jsonInfo.MessageAppHeader.SN, terminal.TerminalMac, terminal.GwMac, jsonInfo.MessageBody.ModuleID, packets.TLVCharConfReqMsg)
		} else {
			globalmemo.NusCharacterDevForHandle.Set(globalutils.CreateCacheKey(terminal.TerminalMac, node.CharUUID), node.CharacterHandle) //特殊处理
		}
	}
	return nil
}

//在解析到特定的广播报文后，进行连接处理
func ConnectBleDev(ctx context.Context, terminal globalstruct.TerminalInfo) error {
	var (
		jsonInfo packets.JsonUdpInfo
		err      error
	)
	globallogger.Log.Infof("<procBleConfirm> DevEui %s start connect operation", terminal.TerminalMac)
	jsonInfo, err = GenerateDownJsonInfo(ctx, &globalstruct.DownJsonInfoUnify{
		GwMac:              terminal.GwMac,
		IotModuleId:        terminal.IotModuleId,
		LinkedMsgType:      packets.IotAccessMsg,
		ApplicationMsgType: packets.BleRequest,
		TlvMsgType:         packets.TLVConnectMsg,
		Ctrl:               globalconstants.CtrlAllMsg,
		DevConnInfo: globalstruct.DevConnection{
			DevMac:            terminal.TerminalMac,
			AddrType:          globalconstants.BLE_CONN_DEV_ADDR_TYPE,
			ScanType:          globalconstants.BLE_CONN_SCAN_TYPE,
			ScanInterval:      globalconstants.BLE_CONN_SCAN_INTERVAL,
			ScanWindow:        globalconstants.BLE_CONN_SCAN_WINDOWS,
			ScanTimeout:       globalconstants.BLE_CONN_SCAN_TIMEOUT,
			ConnInterval:      globalconstants.BLE_CONN_INTERVAL,
			ConnLatency:       globalconstants.BLE_CONN_LATENCY,
			SupervisionWindow: globalconstants.BLE_CONN_TIMEOUT,
		},
	})
	if err != nil {
		globallogger.Log.Errorf("<ConnectBleDev> generate down data failed : %v", err)
		return err
	}
	byteToSend, err := EnCodeForDownUdpMessage(jsonInfo)
	globalutils.BytesToHex(byteToSend)
	if err != nil {
		return errors.New(" <ConnectBleDev> generate byte info failed " + err.Error())
	}
	err = SendMsgBeforeDown(ctx, byteToSend, jsonInfo.MessageHeader.LinkMsgFrameSN, jsonInfo.MessageAppHeader.SN, terminal.TerminalMac, terminal.GwMac, terminal.IotModuleId, packets.TLVConnectMsg)
	if err != nil {
		return err
	}
	//同步检测连接是否超时
	timeOutMark := globalutils.CreateCacheKey(globalconstants.BleDevCacheMessagePrefix, jsonInfo.MessageBody.GwMac, jsonInfo.MessageBody.ModuleID, jsonInfo.MessageAppHeader.SN)
	listCacheKey := globalutils.CreateCacheKey(globalconstants.BleDevCacheMessagePrefix, jsonInfo.MessageBody.GwMac, jsonInfo.MessageBody.ModuleID)
	//不断检测直至超时或者
	timeOutCount := time.NewTimer(globalconstants.BLE_CONN_TIMEOUT / 100 * 6 * time.Second)
	for {
		var queueStr string
		select {
		case <-timeOutCount.C: //时间暂行
			if config.C.General.UseRedis {
				queueStr, err = globalredis.RedisCache.LIndex(ctx, listCacheKey, 0).Result()
				if err != nil {
					return fmt.Errorf("<ConnectBleDev> [DevEui: %s] redis has error %v, frameSN sendDown failed", terminal.TerminalMac, err)
				}
				var headNode storage.NodeCache
				err = json.Unmarshal([]byte(queueStr), &headNode)
				if err != nil {
					return fmt.Errorf("<ConnectBleDev> [DevEui: %s] data has error, frameSN sendDown failed", terminal.TerminalMac)
				}
				if headNode.FirstMark == timeOutMark { //不相等则为等待下发或者已经过了
					globalredis.RedisCache.LPop(ctx, listCacheKey) //超时消息
					return fmt.Errorf("<ConnectBleDev> [DevEui: %s] connect timout", terminal.TerminalMac)
				}
			} else {
				curQueue, ok := globalmemo.MemoCacheDev.Get(listCacheKey)
				if !ok {
					return fmt.Errorf("<ConnectBleDev> [DevEui: %s] memo has error", terminal.TerminalMac)
				}
				if curQueue.(*storage.CQueue).Peek().FirstMark == timeOutMark {
					curQueue.(*storage.CQueue).Dequeue() //超时处理
					return fmt.Errorf("<ConnectBleDev> [DevEui: %s] connect timout", terminal.TerminalMac)
				}
			}
			goto Final
		default:
			time.Sleep(time.Second * 2)
			if config.C.General.UseRedis {
				queueStr, err = globalredis.RedisCache.LIndex(ctx, listCacheKey, 0).Result()
				if err != nil {
					return fmt.Errorf("<ConnectBleDev> [DevEui: %s] redis has error %v, frameSN sendDown failed", terminal.TerminalMac, err)
				}
				var headNode storage.NodeCache
				err = json.Unmarshal([]byte(queueStr), &headNode)
				if err != nil {
					return fmt.Errorf("<ConnectBleDev> [DevEui: %s] data has error %v, frameSN sendDown failed", terminal.TerminalMac, err)
				}
				if headNode.FirstMark != timeOutMark { //不相等则为等待下发或者已经过了
					goto Final
				}
			} else {
				curQueue, ok := globalmemo.MemoCacheDev.Get(listCacheKey)
				if !ok {
					return fmt.Errorf("<ConnectBleDev> [DevEui: %s] memo has error", terminal.TerminalMac)
				}
				if curQueue.(*storage.CQueue).Peek().FirstMark != timeOutMark {
					goto Final
				}
			}
		}
	}
Final:
	return err
}

//特征操作请求参数
func CharacterOptBleDev(ctx context.Context, terminal globalstruct.TerminalInfo, charDownData string) (err error) {
	var charHandle string
	for macUUId, tempCharHandle := range globalmemo.NusCharacterDevForHandle.Items() {
		if len(macUUId) > 6 {
			charHandle = tempCharHandle.(string)
		}
	}
	//charHanle, _ := globalmemo.NusCharacterDevForHandle.Get(terminal.TerminalMac)
	param := globalstruct.ParameterForCharacterDown{
		CharacterHandle: charHandle,
		Parameter:       charDownData,
	}
	jsonInfo, err := GenerateDownJsonInfo(ctx, &globalstruct.DownJsonInfoUnify{
		CharacterOpteration: param,
		TerminalMac:         terminal.TerminalMac,
		GwMac:               terminal.GwMac,
		IotModuleId:         terminal.IotModuleId,
		LinkedMsgType:       packets.IotAccessMsg,
		ApplicationMsgType:  packets.BleRequest,
		TlvMsgType:          packets.TLVCharOptReqMsg,
		Ctrl:                globalconstants.CtrlAllMsg,
	})
	if err != nil {
		return
	}
	byteToSend, err := EnCodeForDownUdpMessage(jsonInfo)
	globalutils.BytesToHex(byteToSend)
	if err != nil {
		return
	}
	err = SendMsgBeforeDown(ctx, byteToSend, jsonInfo.MessageHeader.LinkMsgFrameSN, jsonInfo.MessageAppHeader.SN, terminal.TerminalMac, terminal.GwMac, terminal.IotModuleId, packets.TLVCharOptReqMsg)
	if err != nil {
		return
	}
	return
}
